package com.cb.web.wasf.sample.model;

import com.cb.web.wasf.StringUtils;
import com.cb.web.wasf.sample.model.UniqueConstraintBOException.RollbackOnlyUniqueConstraintBOException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.persist.Transactional;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

/**
 * @author CBO
 */
@Transactional(rollbackOn = RollbackOnlyUniqueConstraintBOException.class)
public class UserManager {

    @Inject
    private RoleManager roleManager;
    @Inject
    Provider<EntityManager> entityManagerProvider;

    public PrincipalImpl authenticate(String login, String password) {
        /*return PersistenceHelper.getThreadLocalEM().createQuery(
        "SELECT u FROM User u WHERE u.login = :login AND u.passwordHash = :passwordHash AND u.enable = :enable",
        User.class).setParameter("login", login).setParameter("passwordHash", hashPassword(password)).setParameter("enable", true).getSingleResult();*/
        CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
        CriteriaQuery<UserBO> query = cb.createQuery(UserBO.class);
        Root<UserBO> u = query.from(UserBO.class);
        query.where(cb.and(cb.equal(u.get(UserBO_.login), login), cb.equal(u.get(UserBO_.passwordHash), hashPassword(password))));
        try {
            UserBO user = entityManagerProvider.get().createQuery(query).getSingleResult();
            return new PrincipalImpl(user, roleManager.getByAllByUser(user));
        } catch (NoResultException nre) {
            return null;
        }
    }

    private int getCount() {
        CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
        CriteriaQuery<Long> query = cb.createQuery(Long.class);
        query.select(cb.count(query.from(UserBO.class)));
        return entityManagerProvider.get().createQuery(query).getSingleResult().intValue();
    }

    public SubList<UserBO> getSubList(int fromIndex, int count) {
        CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
        CriteriaQuery<UserBO> query = cb.createQuery(UserBO.class);
        Root<UserBO> u = query.from(UserBO.class);
        query.orderBy(cb.asc(u.get(UserBO_.firstName)), cb.asc(u.get(UserBO_.lastName)));
        return new SubList(entityManagerProvider.get().createQuery(query).setFirstResult(fromIndex).setMaxResults(count).getResultList(),
                fromIndex, getCount());
    }

    public List<UserBO> getAll() {
        CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
        CriteriaQuery<UserBO> query = cb.createQuery(UserBO.class);
        Root<UserBO> u = query.from(UserBO.class);
        query.orderBy(cb.asc(u.get(UserBO_.firstName)), cb.asc(u.get(UserBO_.lastName)));
        return entityManagerProvider.get().createQuery(query).getResultList();
        /*return PersistenceHelper.getThreadLocalEM().createQuery(
        "SELECT u FROM User u ORDER by u.firstName, u.lastName",
        User.class).getResultList();*/
    }

    public UserBO getById(Long userId) {
        return (UserBO) entityManagerProvider.get().find(UserBO.class, userId);
    }

    public void delete(Long userId) throws BONotFoundException {
        UserBO user = getById(userId);
        if (user != null) {
            try {
                entityManagerProvider.get().remove(user);
            } catch (javax.persistence.EntityNotFoundException enfe) {
                throw new BONotFoundException();
            }
        } else {
            throw new BONotFoundException();
        }
    }

    public UserBO create(String login, String password, String firstName, String lastName, String mail, boolean enable, Collection<ProfilBO> profils) throws UniqueConstraintBOException {
        UserBO user = new UserBO();
        user.setLogin(login);
        user.setPasswordHash(hashPassword(password));
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setMail(mail);
        user.setEnable(enable);
        for (ProfilBO profil : profils) {
            user.getProfils().add(profil);
        }
        try {
            entityManagerProvider.get().persist(user);
        } catch (PersistenceException pe) {
            if (PersistenceExceptionUtils.isUniqueIndexViolation(pe)) {
                throw new RollbackOnlyUniqueConstraintBOException();
            } else {
                throw pe;
            }
        }
        return user;
    }

    public void update(Long userId, String login, String firstName, String lastName, String mail, boolean enable, Collection<ProfilBO> profils) throws BONotFoundException, UniqueConstraintBOException {
        UserBO user = getById(userId);
        if (user != null) {
            user.setLogin(login);
            user.setFirstName(firstName);
            user.setLastName(lastName);
            user.setMail(mail);
            user.setEnable(enable);
            user.getProfils().clear();
            for (ProfilBO profil : profils) {
                user.getProfils().add(profil);
            }
            try {
                entityManagerProvider.get().merge(user);
                entityManagerProvider.get().flush();
            } catch (PersistenceException pe) {
                if (PersistenceExceptionUtils.isUniqueIndexViolation(pe)) {
                    throw new RollbackOnlyUniqueConstraintBOException();
                } else {
                    throw pe;
                }
            }
        } else {
            throw new BONotFoundException();
        }
    }

    private String hashPassword(String password) {
        return StringUtils.hashSHA256(password);
    }
}
